#include "mips.h"

// mips_generate : génère le code MIPS (sépare .data et .text)
//                 par effet de bord
void mips_generate(syntree* tree, gencode* data, gencode* code)
{
	// On analyse un noeud vide, on s'arrête
	if (tree == NULL)
	{
		return;
	}
	
	// Si les données ou le code n'ont pas été allouées, on le fait
	if (data == NULL) data = gencode_init();
	if (code == NULL) code = gencode_init();
	
	int label = labelno++; // Label courant
	symtable* node   = NULL;  // Symbole courant
	syntree*  node_t = NULL; 
	
	// On regarde le type de l'élément courant de l'arbre
	// et on choisit l'action à faire en conséquence
	switch (tree->type)
	{
		// Gestion des types et variables
		//Les types de la forme T_* sont utilisées pour les types de données
		case T_ID :
			node = table_get(table, tree->id.index);
			printf("ID(%s)\n", node->id);
			break;
		
		case T_ID_STR :
			printf("ID(%s)\n", tree->id_str.str);
			break;
		
		case T_TYPE :
			printf("IDENTLIST\tTYPE: ");
			switch (tree->_type.type)
			{
				case T_INT :
					printf("INT");
					break;
				
				case T_BOOL :
					printf("BOOL");
					break;
				
				case T_ARRAY :
					printf("ARRAY");
					break;
				
				default :
					printf("? (%d)", tree->_type.type);
			}
			printf("\n");
			break;
		
		case T_STRING :
			printf("STRING(%s)\n", tree->string.str);
			break;
		
		case T_RANGE :
			printf("RANGE[%d..%d]\n", tree->range.debut, tree->range.fin);
			break;
			
		case T_ARRAY :
			printf("ARRAY[%d..%d] of ", tree->array.debutTab, tree->array.finTab);
			switch (tree->array.type)
			{
				case T_INT :
					printf("INT");
					break;
				
				case T_BOOL :
					printf("BOOL");
					break;
				
				case T_ARRAY :
					printf("ARRAY");
					break;
				
				default :
					printf("? (%d)", tree->array.type);
			}
			printf("\n");
			break;
		
		case T_INT :
			printf("INT(%d)\n", tree->integer.value);
			break;
		
		case T_BOOL :
			printf("BOOL(%s)\n", (tree->boolean.value) ? "TRUE" : "FALSE");
			break;
		
		// Gestion des opérations (if, while, <, etc.)
		//De la forme O_*
		case T_OP :
			switch (tree->op.opr)
			{
				//la fonction mips_generate est récursive jusqu'à ce qu'on arrive
				//à un type de données simple.
				case O_PROGRAM :
					printf("PROGRAM\n");
					// Variables globales
					mips_generate(tree->op.op[0], data, code);
					is_global = 0;
					// Fonctions
					mips_generate(tree->op.op[1], data, code);
					printf("------------------------------------\n");
					// Programme principal
					code = gencode_add(code, "main:\n");
					mips_generate(tree->op.op[2], data, code); 
					break;
				
				case O_VARSDECLLIST :
					printf("VARSDECLLIST\n");
					mips_generate(tree->op.op[0], data, code);
					//gère le nombre d'arguments de la fonction _op
					if (tree->op.nop == 2 && tree->op.op[1] != NULL)
					{
						mips_generate(tree->op.op[1], data, code);
					}
					break;
				
				case O_VAR :
					printf("VAR\n");
					mips_generate(tree->op.op[0], data, code);
					mips_generate(tree->op.op[1], data, code);
					break;
				
				case O_IDENTLIST :
					printf("IDENTLIST\t");
					mips_generate(tree->op.op[0], data, code);
					if (tree->op.nop == 2 && tree->op.op[1] != NULL)
					{
						mips_generate(tree->op.op[1], data, code);
					}
					break;
				
				case O_FUNDECLLIST :
					printf("FUNDECLLIST\n");
					mips_generate(tree->op.op[0], data, code);
					if (tree->op.nop == 2 && tree->op.op[1] != NULL)
					{
						mips_generate(tree->op.op[1], data, code);
					}
					break;
				
				case O_FUNCTION :
					// 0: nom de la fct, 1: liste d'arg, 2: type de retour, 3: var locales 4: instructions
					printf("FUNCTION(%s)\n", tree->op.op[0]->id_str.str);
					// Label de la fonction
					code = gencode_add(code, "function_%s :", tree->op.op[0]->id_str.str);
					// Adresse de retour
					code = gencode_add(code, "add \t$sp, $sp, -4");
					code = gencode_add(code, "sw \t$ra, 0($sp)\n");
					
					// Instructions de la fonction
					mips_generate(tree->op.op[4], data, code);
					
					// Retour de la fonction
					code = gencode_add(code, "lw \t$ra, 0($sp)");
					code = gencode_add(code, "add \t$sp, $sp, 4");
					code = gencode_add(code, "jr \t$ra");
					code = gencode_add(code, "# --\n");
					break;
				
				case O_FUNCCALL :
					printf("FUNCCALL(%s)\n", tree->op.op[0]->id_str.str);
					code = gencode_add(code, "# %s()", tree->op.op[0]->id_str.str);
					code = gencode_add(code, "jal function_%s\n", tree->op.op[0]->id_str.str);
					break;
				
				case O_BEGIN :
					printf("BEGIN {\n");
					if (tree->op.nop == 1)
					{
						mips_generate(tree->op.op[0], data, code);
					}
					printf("BEGIN }\n");
					break;
				
				case O_SEQUENCE :
					mips_generate(tree->op.op[0], data, code);
					printf(";\n");
					if (tree->op.nop == 2)
					{
						mips_generate(tree->op.op[1], data, code);
					}
					
					break;
				
				case O_AFF :
					printf("AFFECT\t");
					mips_generate(tree->op.op[1], data, code);
					//on différencie les cas selon si on affecte 
					//à une variable simple ou à un tableau
					if (tree->op.op[0]->type == T_OP)
					{
						if (tree->op.op[1]->type != T_OP)
						{
							mips_load(tree->op.op[1], "$v0");
						}
						node_t = tree->op.op[0]->op.op[0];
						code = gencode_add(code, "add \t$sp, $sp, -4\n"
						                         "sw \t$v0, 0($sp)");
						mips_generate(tree->op.op[0], data, code);
						code = gencode_add(code, "lw \t$v0, 0($sp)\n"
						                         "add \t$sp, $sp, 4");
						code = gencode_add(code, "# =");
						code = gencode_add(code, "sw \t$v0, 0($s0)\n");
					}
					//pour l'affectation aux variables simples
					else
					{
						mips_generate(tree->op.op[0], data, code);
						node_t = tree->op.op[0];
						node = table_get_str(table, node_t->id.id);
						mips_load(tree->op.op[1], "$v0");
						code = gencode_add(code, "# =");
						code = gencode_add(code, "sw \t$v0, %s\n", node->addr);
					}
					
					break;
					
				case O_EXPRLIST :
					printf("EXPRLIST\n");
					mips_generate(tree->op.op[0], data, code);
					if (tree->op.nop == 2)
					{
						mips_generate(tree->op.op[1], data, code);
					}
					break;
					
				case O_ARRAYACCESS :
					code = gencode_add(code, "# %s[?]", tree->op.op[0]->id_str.str);
					mips_generate(tree->op.op[0], data, code);
					mips_generate(tree->op.op[1], data, code);
					if (tree->op.op[1]->op.op[0]->op.opr < O_PROGRAM)
					{
						mips_load(tree->op.op[1], "$t0");
					}
					else
					{
						mips_load(tree->op.op[1], "$v0");
						code = gencode_add(code, "move \t$t0, $v0");
					}
					node = table_get_str(table, tree->op.op[0]->id_str.str);
					// On gère l'offset en fonction de la valeur de début du
					//tableau, récupérée dans la fonction table_mod_arraylist
					code = gencode_add(code, "sub \t$t0, $t0, %d", node->debut);
					code = gencode_add(code, "la \t$s0, %s", node->addr);
					code = gencode_add(code, "# %s[!]", tree->op.op[0]->id_str.str);
					code = gencode_add(code, "mul \t$s1, $t0, 4\n"
					                         "add \t$s0, $s0, $s1");
					code = gencode_add(code, "lw \t$v0, 0($s0)");
					break;
				
				case O_IF :
					printf("IF (\n");
					if (tree->op.nop == 2)
					{
						code = gencode_add(code, "# IF - THEN");
						mips_generate(tree->op.op[0], data, code);
						mips_load(tree->op.op[0], "$v0");
						printf(") THEN {\n");
						code = gencode_add(code, "beq \t$v0, 0, if_end_%d", label);
						mips_generate(tree->op.op[1], data, code);
						code = gencode_add(code, "if_end_%d :\n", label);
						printf("IF }\n");
					}
					else
					{
						code = gencode_add(code, "# IF - THEN - ELSE");
						mips_generate(tree->op.op[0], data, code);
						mips_load(tree->op.op[0], "$v0");
						printf(") THEN {\n");
						code = gencode_add(code, "beq \t$v0, 0, if_else_%d\n", label);
						mips_generate(tree->op.op[1], data, code);
						printf("} ELSE {\n");
						code = gencode_add(code, "j \tif_end_%d", label);
						code = gencode_add(code, "if_else_%d :", label);
						mips_generate(tree->op.op[2], data, code);
						code = gencode_add(code, "if_end_%d :\n", label);
						printf("IF }\n");
					}
					printf("IF }\n");
					break;
				
				//Toutes les opérations qui suivent fonctionnent de la même manière
				//pour pouvoir fonctionner de façon imbriquée (on stocke une variable
				//dans la pile, et le résultat de l'opération imbriquée est récupérée
				// dans $v0.
				case O_PLUS :
					printf("+\n");
					code = gencode_add(code, "# +");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "add \t$sp, $sp, -4");
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "move \t$t1, $v0");
					code = gencode_add(code, "lw \t$t0, 0($sp)");
					code = gencode_add(code, "add \t$sp, $sp, 4");
					code = gencode_add(code, "add \t$v0, $t0, $t1");
					break;
				
				case O_MINUS :
					if (tree->op.nop == 1) // - unaire
					{
						printf("- (UNARY)\n");
						code = gencode_add(code, "# - (unary)");
						mips_generate(tree->op.op[0], data, code);
						mips_load(tree->op.op[0], "$v0");
						code = gencode_add(code, "li \t$t0, 0");
						code = gencode_add(code, "sub \t$v0, $t0, $v0");
					}
					else // - normal
					{
						printf("-\n");
						code = gencode_add(code, "# -");
						mips_generate(tree->op.op[0], data, code);
						code = gencode_add(code, "add \t$sp, $sp, -4");
						mips_load(tree->op.op[0], "$v0");
						code = gencode_add(code, "sw \t$v0, 0($sp)");
						mips_generate(tree->op.op[1], data, code);
						mips_load(tree->op.op[1], "$v0");
						code = gencode_add(code, "move \t$t1, $v0");
						code = gencode_add(code, "lw \t$t0, 0($sp)");
						code = gencode_add(code, "add \t$sp, $sp, 4");
						code = gencode_add(code, "sub \t$v0, $t0, $t1");
					}
					break;
				
				case O_TIMES :
					printf("*\n");
					code = gencode_add(code, "# *");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "add \t$sp, $sp, -4");
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "move \t$t1, $v0");
					code = gencode_add(code, "lw \t$t0, 0($sp)");
					code = gencode_add(code, "add \t$sp, $sp, 4");
					code = gencode_add(code, "mul \t$v0, $t0, $t1");
					break;
				
				case O_POW :
					printf("^\n");
					code = gencode_add(code, "# ^");
					// On alloue la pile pour les arguments
					code = gencode_add(code, "add \t$sp, $sp, -8");
					// Argument 1
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "sw \t$v0, 0($sp)");					
					mips_generate(tree->op.op[1], data, code);
					code = gencode_add(code, "sw \t$v0, 4($sp)");
					// On appelle _pow dans le MIPS
					code = gencode_add(code, "jal \t_pow");
					// Le résultat est dans $v0, c'est déjà bon
					// On désalloue la pile
					code = gencode_add(code, "add \t$sp, $sp, 8");
					break;
				
				case O_DIV :
					printf("/\n");
					code = gencode_add(code, "# /");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "add \t$sp, $sp, -4");
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "move \t$t1, $v0");
					code = gencode_add(code, "lw \t$t0, 0($sp)");
					code = gencode_add(code, "add \t$sp, $sp, 4");
					code = gencode_add(code, "div \t$t0, $t1\n"
					                         "mflo \t$v0");
					
					break;
				
				case O_MOD :
					printf("%%\n");
					code = gencode_add(code, "# %%");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "add \t$sp, $sp, -4");
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "move \t$t1, $v0");
					code = gencode_add(code, "lw \t$t0, 0($sp)");
					code = gencode_add(code, "add \t$sp, $sp, 4");
					code = gencode_add(code, "div \t$t0, $t1\n"
					                         "mfhi \t$v0");
					break;
					                         
				case O_LT :
					printf("<\n");
					code = gencode_add(code, "# <");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "add \t$sp, $sp, -4");
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "move \t$t1, $v0\n");
					code = gencode_add(code, "lw \t$t0, 0($sp)\n");
					code = gencode_add(code, "add \t$sp, $sp, 4");
					code = gencode_add(code, "slt \t$v0, $t0, $t1\n");
					break;
				
				case O_GE :
					printf(">\n");
					code = gencode_add(code, "# >=");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "add \t$sp, $sp, -8");
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "sw \t$v0, 4($sp)");
					code = gencode_add(code, "jal \t_ge");
					code = gencode_add(code, "add \t$sp, $sp, 8");
					break;
				
				case O_GT :
					printf(">\n");
					code = gencode_add(code, "# >");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "add \t$sp, $sp, -4");
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "move \t$t1, $v0\n");
					code = gencode_add(code, "lw \t$t0, 0($sp)\n");
					code = gencode_add(code, "add \t$sp, $sp, 4");
					code = gencode_add(code, "slt \t$v0, $t1, $t0\n");
					break;
					
				case O_LE :
					printf("<=\n");
					code = gencode_add(code, "# <=");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "add \t$sp, $sp, -8");
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "sw \t$v0, 4($sp)");
					code = gencode_add(code, "jal \t_le");
					code = gencode_add(code, "add \t$sp, $sp, 8");
					break;
				
				case O_NE :
					printf("NE\n");
					code = gencode_add(code, "# NE");
					code = gencode_add(code, "add \t$sp, $sp, -8");
					mips_generate(tree->op.op[0], data, code);
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "sw \t$v0, 4($sp)");
					code = gencode_add(code, "jal \t_ne");
					code = gencode_add(code, "add \t$sp, $sp, 8");
					break;
					
				case O_EQ :
					printf("EQ\n");
					code = gencode_add(code, "# EQ");
					code = gencode_add(code, "add \t$sp, $sp, -8");
					mips_generate(tree->op.op[0], data, code);
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");					
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "sw \t$v0, 4($sp)");
					code = gencode_add(code, "jal \t_eq");
					code = gencode_add(code, "add \t$sp, $sp, 8");
					
					break;
				
				case O_AND :
					printf("/\n");
					code = gencode_add(code, "# AND");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "add \t$sp, $sp, -4");
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "move \t$t1, $v0");
					code = gencode_add(code, "lw \t$t0, 0($sp)");
					code = gencode_add(code, "add \t$sp, $sp, 4");
					code = gencode_add(code, "and \t$v0, $t1, $t0\n");
					
					break;
					
				case O_OR :
					printf("/\n");
					code = gencode_add(code, "# OR");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "add \t$sp, $sp, -4");
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "move \t$t1, $v0");
					code = gencode_add(code, "lw \t$t0, 0($sp)");
					code = gencode_add(code, "add \t$sp, $sp, 4");
					code = gencode_add(code, "or \t$v0, $t0, $t1\n");
					
					break;
					
				case O_XOR :
					printf("/\n");
					code = gencode_add(code, "# XOR");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "add \t$sp, $sp, -4");
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					mips_generate(tree->op.op[1], data, code);
					mips_load(tree->op.op[1], "$v0");
					code = gencode_add(code, "move \t$t1, $v0");
					code = gencode_add(code, "lw \t$t0, 0($sp)");
					code = gencode_add(code, "add \t$sp, $sp, 4");
					code = gencode_add(code, "xor \t$v0, $t0, $t1\n");
					
					break;
					
				case O_NOT :
					printf("NOT\n");
					code = gencode_add(code, "# NOT");
					// Allocation de la pile pour notre argument
					code = gencode_add(code, "add \t$sp, $sp, -4");
					mips_generate(tree->op.op[0], data, code);
					code = gencode_add(code, "sw \t$v0, 0($sp)");
					// On appelle la fonction _not
					code = gencode_add(code, "jal \t_not");
					// Le résultat est dans $v0
					// On désalloue la pile
					code = gencode_add(code, "add \t$sp, $sp, 4");
					break;
				
				case O_WHILE :
					printf("WHILE (\n");
					code = gencode_add(code, "# WHILE");
					code = gencode_add(code, "while_begin_%d :", label);
					mips_generate(tree->op.op[0], data, code);
					mips_load(tree->op.op[0], "$v0");
					code = gencode_add(code, "beq \t$v0, 0, while_end_%d", label); 
					printf(") {\n");
					mips_generate(tree->op.op[1], data, code);
					code = gencode_add(code, "j \twhile_begin_%d", label);
					code = gencode_add(code, "while_end_%d :\n", label);
					printf("WHILE }\n");
					break;
				
				case O_READ :
					printf("READ\t");
					
					//on différencie le READ pour un tableau ou une simple variable
					if (tree->op.op[0]->op.opr == O_ARRAYACCESS)
					{
						printf("[]\n");
						code = gencode_add(code, "# %s[?]", tree->op.op[0]->op.op[0]->id_str.str);
						mips_generate(tree->op.op[0]->op.op[0], data, code);
						mips_generate(tree->op.op[0]->op.op[1], data, code);
						mips_load(tree->op.op[0]->op.op[1], "$t0");
						node = table_get_str(table, tree->op.op[0]->op.op[0]->id_str.str);
						code = gencode_add(code, "la \t$s0, %s", node->addr);
						code = gencode_add(code, "# %s[!]", tree->op.op[0]->op.op[0]->id_str.str);
						code = gencode_add(code, "mul \t$s1, $t0, 4\n"
							                     "add \t$s0, $s0, $s1");
						code = gencode_add(code, "lw \t$v0, 0($s0)");
						
						code = gencode_add(code, "# read id[] \n"
						                         "la \t$a0, entrez_entier\n"
												 "jal \t_print_str\n"
											     "move \t$a0, $v0\n"
											     "jal \t_read_int\n", node->addr);
						code = gencode_add(code, "sw \t$v0, 0($s0)\n");
					}
					else
					{
						node = table_get(table, tree->op.op[0]->id.index);
						// switch sur le type de la variable à lire
						switch (node->type)
						{
							case T_INT :
								code = gencode_add(code, "# read id(int) \n"
														 "la \t$a0, entrez_entier\n"
														 "jal \t_print_str\n"
											             "lw \t$a0, %s\n"
											             "jal \t_read_int\n", node->addr);
								code = gencode_add(code, "# = \n"
									                     "sw $v0, %s\n", node->addr);
							
								break;
						
							case T_BOOL :
								code = gencode_add(code, "# read id(bool) \n"
														 "la \t$a0, entrez_booleen\n"
														 "jal \t_print_str\n"
										                 "lw \t$a0, %s\n"
												         "jal \t_read_int\n", node->addr);
								code = gencode_add(code, "# = \n"
									                     "sw $v0, %s\n", node->addr);
								break;
						
							default :
								printf("READ: type de variable inconnu\n");
						}
						mips_generate(tree->op.op[0], data, code);
					}
					break;
				
				case O_WRITE :
					printf("WRITE\t");
					
					mips_generate(tree->op.op[0], data, code);
					
					// On regarde le type du noeud à écrire
					switch (tree->op.op[0]->type)
					{
						case T_ID :
							node = table_get(table, tree->op.op[0]->id.index);
							//switch sur le type de la variable à écrire
							switch (node->type)
							{
								case T_INT :
									code = gencode_add(code, "# print id \n"
											                 "lw \t$a0, %s\n"
											                 "jal \t_print_int\n", node->addr);
									break;
								
								case T_BOOL :
									code = gencode_add(code, "# print bool \n"
										                     "lw \t$a0, %s\n"
												             "jal \t_print_bool\n", node->addr);
									break;
									
								
								default :
									printf("WRITE: type de variable inconnu\n");
							}
							break;
							
						case T_STRING :
							data = gencode_add(data, "%s :\n"
							                         "\t.asciiz %s", tree->op.op[0]->string.label, tree->op.op[0]->string.str);
							                         
							code = gencode_add(code, "# print string \n"
							                         "la \t$a0, %s\n"
							                         "jal \t_print_str\n", tree->op.op[0]->string.label);
							break;
						
						case T_INT :
							code = gencode_add(code, "# print int \n"
							                         "li \t$a0, %d\n"
							                         "jal \t_print_int\n", tree->op.op[0]->integer.value);
							break;
						
						case T_BOOL :
							if (tree->op.op[0]->boolean.value)
							{
								code = gencode_add(code, "# print bool \n"
								                         "la \t$a0, str_true\n"
								                         "jal \t_print_str\n");
							}
							else
							{
								code = gencode_add(code, "# print bool \n"
								                         "la \t$a0, str_false\n"
								                         "jal \t_print_str\n");
							}
							break;
						
						case T_OP :
							code = gencode_add(code, "# print id[]\n"
							                         "move \t$a0, $v0\n"
							                         "jal \t_print_int\n");
							break;
						
						default :
							printf("? (%d)\n", tree->op.op[0]->type);
					}
					
					break;
				
				default :
					printf("Opération non gérée ! (%d)\n", tree->op.opr);
					break;
				
			}
			break;
		
		default :
			printf("Type de noeud inconnu (%d)\n", tree->type);
	}
}

// mips_write : écrit le code MIPS dans le fichier OUT
void mips_write(char* output_file, gencode* data, gencode* code)
{
	FILE*         out = NULL;
	gencode_node* tmp = NULL;
	
	// Ouverture du fichier en sortie
	if ((out = fopen(output_file, "w+")) == NULL)
	{
		printf("Erreur : impossible de créer en écriture "
		       "le fichier '%s'\n", output_file);
		exit(-1);
	}
	
	// On écrit les fonctions, chaines de caractères, et code nécessaire
	// à notre code MIPS pour qu'il fonctionne
	
	fprintf(out, "# MIPS generated from SCALPA program %s\n"
	             "\t.data\n\n", input_file);
	
	fprintf(out, "str_true :\n"
	             "\t.asciiz \"TRUE\"\n");
	
	fprintf(out, "str_false :\n"
	             "\t.asciiz \"FALSE\"\n");
	
	fprintf(out, "entrez_entier :\n"
	             "\t.asciiz \"Entrez un entier : \"\n");
	             
	fprintf(out, "entrez_booleen :\n"
	             "\t.asciiz \"Entrez un booleen (0 => FALSE, 1 => TRUE) : \"\n");
	
	// Parcours de data
	if (data != NULL)
	{
		tmp = data->first;
		
		while (tmp != NULL)
		{
			fprintf(out, "%s\n", tmp->str);
			tmp = tmp->next;
		}
	}
	
	fprintf(out, "\n\t.text\n\n");
	
	// Fonctions
	
	// Affichage d'un entier
	fprintf(out, "_print_int: # use $a0\n"
	             "li \t$v0, 1\n"
	             "syscall\n"
	             "jr \t$ra\n\n");
	
	// Affichage d'une chaîne de caractères
	fprintf(out, "_print_str: # use $a0\n"
	            "li \t$v0, 4\n"
	            "syscall\n"
	            "jr \t$ra\n\n");
	
	// Affichage d'un booléen
	fprintf(out, "_print_bool: # use $a0\n"
	             "beq \t$a0, 0, print_false\n"
	             "la \t$a0, str_true\n"
	             "j print_bool\n"
	             "print_false:\n"
	             "la \t$a0, str_false\n"
	             "print_bool:\n"
	             "li \t$v0, 4\n"
	             "syscall\n"
	             "jr \t$ra\n\n");
	
	// Inégalité ( <> )
	fprintf(out, "_ne :\n"
	             "add \t$sp, $sp, -4\n"
	             "sw \t$ra, 0($sp)\n"
	             "lw \t$t0, 4($sp)\n"
	             "lw \t$t1, 8($sp)\n"
	             "bne $t0, $t1, if_neq_ne\n"
	             "li \t$v0, 0\n"
	             "lw \t$ra, 0($sp)\n"
	             "add \t$sp, $sp, 4\n"
	             "jr \t$ra\n"
	             "if_neq_ne :\n"
	             "li \t$v0, 1\n"
	             "lw \t$ra, 0($sp)\n"
	             "add \t$sp, $sp, 4\n"
	             "jr \t$ra\n\n");
	
	// Égalité ( = )       
	fprintf(out, "_eq :\n"
				 "add \t$sp, $sp, -4\n"
				 "# 0($sp) : adresse de retour\n"
	             "sw \t$ra, 0($sp)\n\n"
	             "lw \t$t0, 4($sp)\n"
	             "lw \t$t1, 8($sp)\n"
	             
	             "li \t$t2, 0\n"
				 "li \t$t3, 1\n"
	             
	             "beq $t0, $t1, if_eq_eq\n"
	             "move \t$v0, $t2\n"
	             "j \tend_eq\n"
	             "if_eq_eq :\n"
	             "move \t$v0, $t3\n\n"
	             "end_eq :\n"
	             "lw \t$ra, 0($sp)\n"
	             "add \t$sp, $sp, 4\n\n"
	             "jr \t$ra\n\n");
	
	// Supérieur ou égal ( >= )            
	fprintf(out, "_ge :\n"
				 "add \t$sp, $sp, -4\n"
				 "# 0($sp) : adresse de retour\n"
	             "sw \t$ra, 0($sp)\n\n"
	             "lw \t$t0, 4($sp)\n"
	             "lw \t$t1, 8($sp)\n"
	             "li \t$t2, 0\n"
				 "li \t$t3, 1\n"
	             
	             "bgt $t1, $t0, if_gt_ge\n"
	             "move \t$v0, $t3\n"
	             "j \tend_ge\n"
	             
	             "if_gt_ge :\n"
	             "move \t$v0, $t2\n\n"
	             
	             "end_ge :\n"
	             "lw \t$ra, 0($sp)\n"
	             "add \t$sp, $sp, 4\n\n"
	             "jr \t$ra\n\n");
	
	// Inférieur ou égal ( <= )         
	fprintf(out, "_le :\n"
				 "add \t$sp, $sp, -4\n"
				 "# 0($sp) : adresse de retour\n"
	             "sw \t$ra, 0($sp)\n\n"
	             "lw \t$t0, 4($sp)\n"
	             "lw \t$t1, 8($sp)\n"
	             "li \t$t2, 0\n"
				 "li \t$t3, 1\n"
	             
	             "bgt $t0, $t1, if_gt_le\n"
	             "move \t$v0, $t3\n"
	             "j \tend_le\n"
	             
	             "if_gt_le :\n"
	             "move \t$v0, $t2\n\n"
	             
	             "end_le :\n"
	             "lw \t$ra, 0($sp)\n"
	             "add \t$sp, $sp, 4\n\n"
	             "jr \t$ra\n\n");
	
	// Non logique (not)
	fprintf(out, "_not :\n"
				 "add \t$sp, $sp, -4\n"
	             "# 0($sp) : adresse de retour\n"
	             "sw \t$ra, 0($sp)\n"
				 "# variables locales t0, t1 valant 0 et 1\n"
				 "li \t$t0, 0\n"
				 "li \t$t1, 1\n"
				 "# la valeur de notre variable stockee dans la pile est chargée dans t2\n"
				 "lw \t$t2, 4($sp)\n\n"
				 "# si t2 est different de 0, on va a else_not, sinon on stock 1 (true) dans v0\n"
	             "bne \t$t2, $t0, else_not\n"
	             "move \t$v0, $t1\n"
	             "j \tend\n\n"
	             "# on est dans else_not donc on stock 0 (false) dans v0\n"
	             "else_not :\n"
	             "move \t$v0, $t0\n\n"
	             "# fin, on recharge $ra et on desalloue la pile\n"
	             "end :\n"
	             "lw \t$ra, 0($sp)\n"
	             "add \t$sp, $sp, 4\n\n"
	             "jr $ra\n\n");
	
	// Puissance ( ^ )
	fprintf(out, "_pow :\n"
	             "# On alloue 3 mots dans la pile\n"
	             "add \t$sp, $sp, -12\n"
	             "# 0($sp) : adresse de retour\n"
	             "sw \t$ra, 0($sp)\n"
	             "# 4($sp) : i (init 0)\n"
	             "li \t$t0, 0\n"
	             "sw \t$t0, 4($sp)\n"
	             "# 8($sp) : resultat (init 1)\n"
	             "li \t$t0, 1\n"
	             "sw \t$t0, 8($sp)\n\n"
	             
	             "while_pow:\n"
	             "# Condition d'arrêt (i >= n)\n"
	             "lw \t$t0, 4($sp) # i\n"
	             "lw \t$t1, 16($sp)	# n\n"
	             "bge \t$t0, $t1, endwhile_pow\n\n"
	             
	             "# Contenu de la boucle\n"
	             "# resultat *= x\n"
	             "lw \t$t0, 12($sp)	# x\n"
	             "lw \t$t1, 8($sp) # resultat\n"
	             "mul \t$t1, $t1, $t0	# resultat = resultat * x\n\n"
	             
	             "# On sauve le résultat dans la pile\n"
	             "sw \t$t1, 8($sp)\n\n"
	             
	             "# i++\n"
	             "lw \t$t0, 4($sp) # i\n"
	             "add \t$t0, $t0, 1	# i = i + 1\n"
	             "sw $t0, 4($sp)\n\n"
	             
	             "j \twhile_pow\n"
	             "endwhile_pow:\n"
	             "# --\n\n"
	             
	             "# On met le résultat dans $v0 (valeur de retour)\n\n"
	             "lw \t$t0, 8($sp)\n"
	             "move $v0, $t0\n"
	             
	             "# On recharge l'adresse de retour\n"
	             "lw \t$ra, 0($sp)\n\n"
	             
	             "# On désalloue les 3 mots\n"
	             "add \t$sp, $sp, 12\n\n"
	             
	             "# On revient\n"
	             "jr $ra\n\n");
	
	// Lire un entier
	fprintf(out, "_read_int: # return $v0 \n"
	            "li \t$v0, 5\n"
	            "syscall\n"
	            "jr \t$ra\n\n");
	
	// Parcours du code
	if (code != NULL)
	{
		tmp = code->first;
		
		while (tmp != NULL)
		{
			fprintf(out, "%s\n", tmp->str);
			tmp = tmp->next;
		}
	}
	
	//on quitte le programme
	fprintf(out, "# Exit\n"
	             "li\t$v0, 10\n"
	             "syscall\n\n");
	
}

// mips_load : charge dans ADDR la node de l'arbre (valeur ou addresse)
void mips_load(syntree* node, char* addr)
{	
	// Selon le type du noeud, on charge différemment la valeur dans ADDR
	switch (node->type)
	{
		case T_INT :
			code = gencode_add(code, "li \t%s, %d", addr, node->integer.value);
			break;
		
		case T_BOOL :
			code = gencode_add(code, "li \t%s, %d", addr, node->boolean.value);
			break;
		
		case T_ID :
			code = gencode_add(code, "lw \t%s, %s", addr, table_get(table, node->id.index)->addr);
			break;
		
		case T_ID_STR:
			code = gencode_add(code, "lw \t%s, %s", addr, table_get_str(table, node->id_str.str)->addr);
			break;
		
		case T_OP :
			if (node->op.opr == O_EXPRLIST)
			{
				mips_load(node->op.op[0], addr);
			}
			break;
		
		default:
			printf("LOAD: Type non reconnu (%d)\n", node->type);
	}
}

